package ai.turbochain.ipex.controller;

import static ai.turbochain.ipex.constant.SysConstant.SESSION_MEMBER;
import static ai.turbochain.ipex.util.BigDecimalUtils.compare;
import static ai.turbochain.ipex.util.BigDecimalUtils.sub;
import static ai.turbochain.ipex.util.MessageResult.error;
import static org.springframework.util.Assert.hasText;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;

import com.alibaba.fastjson.JSONObject;

import ai.turbochain.ipex.constant.BooleanEnum;
import ai.turbochain.ipex.constant.CommonStatus;
import ai.turbochain.ipex.constant.SysConstant;
import ai.turbochain.ipex.constant.WithdrawStatus;
import ai.turbochain.ipex.entity.Coin;
import ai.turbochain.ipex.entity.Member;
import ai.turbochain.ipex.entity.MemberAddress;
import ai.turbochain.ipex.entity.MemberWallet;
import ai.turbochain.ipex.entity.ScanMemberAddress;
import ai.turbochain.ipex.entity.ScanWithdrawRecord;
import ai.turbochain.ipex.entity.WithdrawRecord;
import ai.turbochain.ipex.entity.WithdrawWalletInfo;
import ai.turbochain.ipex.entity.transform.AuthMember;
import ai.turbochain.ipex.exception.InformationExpiredException;
import ai.turbochain.ipex.service.CoinService;
import ai.turbochain.ipex.service.LocaleMessageSourceService;
import ai.turbochain.ipex.service.MemberAddressService;
import ai.turbochain.ipex.service.MemberService;
import ai.turbochain.ipex.service.MemberTransactionService;
import ai.turbochain.ipex.service.MemberWalletService;
import ai.turbochain.ipex.service.WithdrawRecordService;
import ai.turbochain.ipex.util.Md5;
import ai.turbochain.ipex.util.MessageResult;
import lombok.extern.slf4j.Slf4j;

/**
 * @author jack
 * @date 2018年01月26日
 */
@RestController
@Slf4j
@RequestMapping(value = "/withdraw", method = RequestMethod.POST)
public class WithdrawController {
	@Autowired
	private MemberAddressService memberAddressService;
	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private MemberService memberService;
	@Autowired
	private CoinService coinService;
	@Autowired
	private MemberWalletService memberWalletService;
	@Autowired
	private WithdrawRecordService withdrawApplyService;
	@Autowired
	private KafkaTemplate<String, String> kafkaTemplate;
	@Autowired
	private LocaleMessageSourceService sourceService;
	@Autowired
	private MemberTransactionService memberTransactionService;

	/**
	 * 增加提现地址
	 * 
	 * @param address
	 * @param unit
	 * @param remark
	 * @param code
	 * @param aims
	 * @param user
	 * @return
	 */
	@RequestMapping("address/add")
	@Transactional(rollbackFor = Exception.class)
	public MessageResult addAddress(String address, String unit, String remark, String code, String aims,
			@SessionAttribute(SESSION_MEMBER) AuthMember user) {
		hasText(address, sourceService.getMessage("MISSING_COIN_ADDRESS"));
		hasText(unit, sourceService.getMessage("MISSING_COIN_TYPE"));
		hasText(code, sourceService.getMessage("MISSING_VERIFICATION_CODE"));
		hasText(aims, sourceService.getMessage("MISSING_PHONE_OR_EMAIL"));
		ValueOperations valueOperations = redisTemplate.opsForValue();
		Member member = memberService.findOne(user.getId());
		if (member.getEmail() != null && aims.equals(member.getEmail())) {
			Object info = valueOperations.get(SysConstant.EMAIL_WITHDRAW_ADDRESS_CODE_PREFIX + member.getEmail());
			if (!info.toString().equals(code)) {
				return MessageResult.error(sourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
			} else {
				valueOperations.getOperations()
						.delete(SysConstant.EMAIL_WITHDRAW_ADDRESS_CODE_PREFIX + member.getEmail());
			}
		} else {
			return MessageResult.error(sourceService.getMessage("ADD_ADDRESS_FAILED"));
		}
		MessageResult result = memberAddressService.addMemberAddress(user.getId(), address, unit, remark);
		if (result.getCode() == 0) {
			result.setMessage(sourceService.getMessage("ADD_ADDRESS_SUCCESS"));
		} else if (result.getCode() == 500) {
			result.setMessage(sourceService.getMessage("ADD_ADDRESS_FAILED"));
		} else if (result.getCode() == 600) {
			result.setMessage(sourceService.getMessage("COIN_NOT_SUPPORT"));
		}
		return result;
	}

	/**
	 * 删除提现地址
	 * 
	 * @param id
	 * @param user
	 * @return
	 */
	@RequestMapping("address/delete")
	@Transactional(rollbackFor = Exception.class)
	public MessageResult deleteAddress(long id, @SessionAttribute(SESSION_MEMBER) AuthMember user) {
		MessageResult result = memberAddressService.deleteMemberAddress(user.getId(), id);
		if (result.getCode() == 0) {
			result.setMessage(sourceService.getMessage("DELETE_ADDRESS_SUCCESS"));
		} else {
			result.setMessage(sourceService.getMessage("DELETE_ADDRESS_FAILED"));
		}
		return result;
	}

	/**
	 * 提现地址分页信息
	 * 
	 * @param user
	 * @param pageNo
	 * @param pageSize
	 * @param unit
	 * @return
	 */
	@RequestMapping("address/page")
	public MessageResult addressPage(@SessionAttribute(SESSION_MEMBER) AuthMember user, int pageNo, int pageSize,
			String unit) {
		Page<MemberAddress> page = memberAddressService.pageQuery(pageNo, pageSize, user.getId(), unit);
		Page<ScanMemberAddress> scanMemberAddresses = page.map(x -> ScanMemberAddress.toScanMemberAddress(x));
		MessageResult result = MessageResult.success();
		result.setData(scanMemberAddresses);
		return result;
	}

	/**
	 * 支持提现的地址
	 * 
	 * @return
	 */
	@RequestMapping("support/coin")
	public MessageResult queryWithdraw() {
		List<Coin> list = coinService.findAllCanWithDraw();
		List<String> list1 = new ArrayList<>();
		list.stream().forEach(x -> list1.add(x.getUnit()));
		MessageResult result = MessageResult.success();
		result.setData(list1);
		return result;
	}

	/**
	 * 提现币种详细信息
	 * 
	 * @param user
	 * @return
	 */
	@RequestMapping("support/coin/info")
	public MessageResult queryWithdrawCoin(@SessionAttribute(SESSION_MEMBER) AuthMember user) {
		List<Coin> list = coinService.findAllCanWithDraw();
		List<MemberWallet> list1 = memberWalletService.findAllByMemberId(user.getId());
		long id = user.getId();
		List<WithdrawWalletInfo> list2 = list1.stream().filter(x -> list.contains(x.getCoin()))
				.map(x -> WithdrawWalletInfo.builder().balance(x.getBalance())
						.withdrawScale(x.getCoin().getWithdrawScale()).maxTxFee(x.getCoin().getMaxTxFee())
						.minTxFee(x.getCoin().getMinTxFee()).minAmount(x.getCoin().getMinWithdrawAmount())
						.maxAmount(x.getCoin().getMaxWithdrawAmount()).name(x.getCoin().getName())
						.nameCn(x.getCoin().getNameCn()).threshold(x.getCoin().getWithdrawThreshold())
						.unit(x.getCoin().getUnit()).canAutoWithdraw(x.getCoin().getCanAutoWithdraw())
						.addresses(memberAddressService.queryAddress(id, x.getCoin().getName())).build())
				.collect(Collectors.toList());
		MessageResult result = MessageResult.success();
		result.setData(list2);
		return result;
	}

	/**
	 * 申请提币(请到PC端提币或升级APP) 没有验证码校验
	 * 
	 * @param user
	 * @param unit
	 * @param address
	 * @param amount
	 * @param fee
	 * @param remark
	 * @param jyPassword
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("apply")
	@Transactional(rollbackFor = Exception.class)
	public MessageResult withdraw(@SessionAttribute(SESSION_MEMBER) AuthMember user, String unit, String address,
			BigDecimal amount, BigDecimal fee, String remark, String jyPassword) throws Exception {
		return MessageResult.success(sourceService.getMessage("WITHDRAW_TO_PC"));
	}

	/**
	 * 申请提币（添加验证码校验） 暂时去掉验证码
	 * 
	 * @param user
	 * @param unit
	 * @param address
	 * @param amount
	 * @param fee
	 * @param remark
	 * @param jyPassword
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("apply/code")
	@Transactional(rollbackFor = Exception.class)
	public MessageResult withdrawCode(@SessionAttribute(SESSION_MEMBER) AuthMember user, String unit, String address,
			BigDecimal amount, BigDecimal fee, String remark, String jyPassword, @RequestParam("code") String code)
			throws Exception {
		hasText(jyPassword, sourceService.getMessage("MISSING_JYPASSWORD"));
		hasText(unit, sourceService.getMessage("MISSING_COIN_TYPE"));
		Coin coin = coinService.findByUnit(unit);
		amount.setScale(coin.getWithdrawScale(), BigDecimal.ROUND_DOWN);
		notNull(coin, sourceService.getMessage("COIN_ILLEGAL"));

		isTrue(coin.getStatus().equals(CommonStatus.NORMAL) && coin.getCanWithdraw().equals(BooleanEnum.IS_TRUE),
				sourceService.getMessage("COIN_NOT_SUPPORT"));
		isTrue(compare(fee, new BigDecimal(String.valueOf(coin.getMinTxFee()))),
				sourceService.getMessage("CHARGE_MIN") + coin.getMinTxFee());
		isTrue(compare(new BigDecimal(String.valueOf(coin.getMaxTxFee())), fee),
				sourceService.getMessage("CHARGE_MAX") + coin.getMaxTxFee());
		isTrue(compare(coin.getMaxWithdrawAmount(), amount),
				sourceService.getMessage("WITHDRAW_MAX") + coin.getMaxWithdrawAmount());
		isTrue(compare(amount, coin.getMinWithdrawAmount()),
				sourceService.getMessage("WITHDRAW_MIN") + coin.getMinWithdrawAmount());
		MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coin, user.getId());
		isTrue(compare(memberWallet.getBalance(), amount), sourceService.getMessage("INSUFFICIENT_BALANCE"));
//        isTrue(memberAddressService.findByMemberIdAndAddress(user.getId(), address).size() > 0, sourceService.getMessage("WRONG_ADDRESS"));
		isTrue(memberWallet.getIsLock() == BooleanEnum.IS_FALSE, "钱包已锁定");
		Member member = memberService.findOne(user.getId());
		String mbPassword = member.getJyPassword();
		Assert.hasText(mbPassword, sourceService.getMessage("NO_SET_JYPASSWORD"));
		Assert.isTrue(Md5.md5Digest(jyPassword + member.getSalt()).toLowerCase().equals(mbPassword),
				sourceService.getMessage("ERROR_JYPASSWORD"));
		ValueOperations valueOperations = redisTemplate.opsForValue();
		Object codeRedis = valueOperations.get(SysConstant.EMAIL_WITHDRAW_CODE_PREFIX + member.getEmail());
		notNull(codeRedis, sourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
		if (!codeRedis.toString().equals(code)) {
			return error(sourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
		} else {
			valueOperations.getOperations().delete(SysConstant.EMAIL_WITHDRAW_CODE_PREFIX + member.getEmail());
		}
		MessageResult result = memberWalletService.freezeBalance(memberWallet, amount);
		if (result.getCode() != 0) {
			throw new InformationExpiredException("Information Expired");
		}
		WithdrawRecord withdrawApply = new WithdrawRecord();
		withdrawApply.setCoin(coin);
		withdrawApply.setFee(fee);
		withdrawApply.setArrivedAmount(sub(amount, fee));
		withdrawApply.setMemberId(user.getId());
		withdrawApply.setTotalAmount(amount);
		withdrawApply.setAddress(address);
		withdrawApply.setRemark(remark);
		withdrawApply.setCanAutoWithdraw(coin.getCanAutoWithdraw());

		// 提币数量低于或等于阈值并且该币种支持自动提币
		if (amount.compareTo(coin.getWithdrawThreshold()) <= 0
				&& coin.getCanAutoWithdraw().equals(BooleanEnum.IS_TRUE)) {
			withdrawApply.setStatus(WithdrawStatus.WAITING);
			withdrawApply.setIsAuto(BooleanEnum.IS_TRUE);
			withdrawApply.setDealTime(withdrawApply.getCreateTime());
			WithdrawRecord withdrawRecord = withdrawApplyService.save(withdrawApply);
			JSONObject json = new JSONObject();
			json.put("uid", user.getId());
			// 提币总数量
			json.put("totalAmount", amount);
			// 手续费
			json.put("fee", fee);
			// 预计到账数量
			json.put("arriveAmount", sub(amount, fee));
			// 币种
			json.put("coin", coin);
			// 提币地址
			json.put("address", address);
			// 提币记录id
			json.put("withdrawId", withdrawRecord.getId());
			kafkaTemplate.send("withdraw", coin.getUnit(), json.toJSONString());
			return MessageResult.success(sourceService.getMessage("APPLY_SUCCESS"));
		} else {
			withdrawApply.setStatus(WithdrawStatus.PROCESSING);
			withdrawApply.setIsAuto(BooleanEnum.IS_FALSE);
			if (withdrawApplyService.save(withdrawApply) != null) {
				return MessageResult.success(sourceService.getMessage("APPLY_AUDIT"));
			} else {
				throw new InformationExpiredException("Information Expired");
			}
		}
	}

	/**
	 * 提币记录
	 * 
	 * @param user
	 * @param page
	 * @param pageSize
	 * @return
	 */
	@GetMapping("record")
	public MessageResult pageWithdraw(@SessionAttribute(SESSION_MEMBER) AuthMember user, int page, int pageSize) {
		MessageResult mr = new MessageResult(0, "success");
		Page<WithdrawRecord> records = withdrawApplyService.findAllByMemberId(user.getId(), page, pageSize);
		records.map(x -> ScanWithdrawRecord.toScanWithdrawRecord(x));
		mr.setData(records);
		return mr;
	}

}
